
#include "net_protocol_impl.h"
#include "timer_object.h"

using namespace WtfEngine;


/**** Manager States ****/


/**
 * This state waits for the specified timeout before setting the
 * state to the next state specified.
 **/
class WaitState: public PeerManager::State {
public:
	WaitState(PeerManager::State::tRefa rNextState, tGameTime tmo)
	{
		new Timer(this, (new ObjFunctor<WeakRef<PeerManager>, void, PeerManager::State::tRefa>(
			PeerManager::GetInstancePtr(), &PeerManager::setState))->Bind(rNextState), tmo);
	};
};


/**
 * In this state, the node broadcasts on the LAN, then
 * enters a broadcast/loop cycle with a timeout, until the
 * state is changed by a connecting peer.
 **/
class BroadcastState: public PeerManager::State {
	UdpBroadcastSocket::tRef mrSocket;

public:
	BroadcastState(UdpBroadcastSocket::tRefa rSocket, Party::tRefa rParty)
		: mrSocket(rSocket)
	{
	};
	virtual ~BroadcastState() {};

	virtual void Process() {
		mrSocket->SendPacket(new Packet<PartyBroadcastMessage>(
			PeerManager::GetInstance().getParty()->CreateBroadcastMessage()));
		PeerManager::GetInstance().setState(new WaitState(this, 5.0));
	};

	GC_AUTOSIZE(BroadcastState);
};


class TransactionState: public PeerManager::State {
	Peer::tRef mrPeer;
	PeerManager::State::tRef mrPreviousState;

public:
	TransactionState(Peer::tRefa rPeer)
		: mrPeer(rPeer)
	{
		if(typeid(*PeerManager::GetInstance().getState()) == typeid(TransactionState)) {
			throw std::logic_error("Party is already in transaction");
		};
	};

	void Exit(Peer * pPeer) {
		if(mrPeer == pPeer) {
			PeerManager::GetInstance().setState(mrPreviousState);
		};
	};
};



/**** The Peer Manager ****/

PeerManager::PeerManager()
: Task<PeerManager>(0, "Network"), mrControlPortSetting("v_net_tcp_port"),
  mrDataPortSetting("v_net_udp_port")
{
	// Create the control socket and data socket
	mrControlSocket = new TcpSocket(mrControlPortSetting->Value);
	mrDataSocket = new UdpSocket(mrDataPortSetting->Value);

	Kernel::GetInstance().BindEvent(new SocketClientConnectEvent(mrControlSocket),
		new ObjFunctor<PeerManager>(this, &PeerManager::OnControlSocketConnect));

	Kernel::GetInstance().BindEvent(new SocketDataEvent(mrDataSocket),
		new ObjFunctor<PeerManager>(this, &PeerManager::OnData));

	mrControlSocket->Listen();
	mrDataSocket->Listen();
};


/**
 * Called when a client connects to the control socket.
 **/
void PeerManager::OnControlSocketConnect()
{
	SocketClientConnectEvent * pEv = Kernel::GetInstance().GetCurrentEvent<SocketClientConnectEvent>();
	assert(pEv);

	// TODO: better system for allocation
	static unsigned uNodeId = 0; 
	mvAllPeers[uNodeId] = new Peer(uNodeId, pEv->getNewSocket(), Peer::UNIDENTIFIED);
	uNodeId++;
};

void PeerManager::OnData()
{
	SocketDataEvent * pEv = Kernel::GetInstance().GetCurrentEvent<SocketDataEvent>();
	assert(pEv);

	// We expect a UDP datagram, so this should be the whole message
	Packet<>::tRef rPacket = new Packet<>(pEv->getSocket()->RecvPacket()->getData());
	if((*rPacket)->isValidPacket()) {
		std::map<unsigned, Peer::tRef>::const_iterator i = mvAllPeers.find((*rPacket)->NodeId);
		if(i != mvAllPeers.end()) {
			i->second->ProcessPacket(rPacket);
		};
	};
};

void PeerManager::Run()
{
	mrState->Process();
	for(std::map<unsigned, Peer::tRef>::const_iterator i = mvAllPeers.begin(); i != mvAllPeers.end(); i++) {
		i->second->Process();
	};
};
